﻿//原型功能补充及兼容性修正
//css函数的selection函数需要tools.js模块，animate函数需要Animate.js模块

window.getScrollOffset = function () {//返回滚动条偏移量,兼容修正
    var w = this;

    if (w.pageXOffset != null) return { x: w.pageXOffset, y: w.pageYOffset };
};

window.getViewportSize = function () {//返回视口大小
    var w = this;
    if (w.innerWidth != null) return { w: w.innerWidth, h: w.innerHeight };

};

Element.prototype.getComputedStyle = function (pseudoEle) {//获得元素计算css属性
    var pe = pseudoEle || "";

    var obj = this;
    if (window.getComputedStyle) return window.getComputedStyle(obj, pe);
    return this.currentStyle;
};

Element.prototype.realBoxSize = function () {//返回元素实际长宽
    return wkh.tools.realBoxSize(this);
};

Element.prototype.getBoxSize = function (boxSizing) {//返回盒子尺寸信息,参数值为 content-box,border-box,padding-box,margin-box
    var boxE = this.getBoundingClientRect();
    var scroll = window.getScrollOffset();

    var box = {
        top: boxE.top + scroll.y,
        bottom: boxE.bottom + scroll.y,
        left: boxE.left + scroll.x,
        right: boxE.right + scroll.x,
        width: boxE.width || boxE.right - boxE.left,
        height: boxE.height || boxE.bottom - boxE.top
    };

    function getBorderWidth(css) {
        if (!window.getComputedStyle) {
            var value = style[css];
            if (style[css.slice(0, -5) + "Style"] === "none") {
                return 0;
            } else {
                switch (value) {
                    case "thin": return 1;
                    case "medium": return 3;
                    case "thick": return 5;
                    default: return parseFloat(style[css]);
                }
            }
        } else {
            return parseFloat(style[css]);
        }
    }

    var style = this.getComputedStyle();
    boxSizing = boxSizing || "border-box";
    switch (boxSizing) {
        case "content-box":
            box.width = box.width - getBorderWidth("borderLeftWidth") - getBorderWidth("borderRightWidth") - parseFloat(style.paddingLeft) - parseFloat(style.paddingRight);
            box.height = box.height - getBorderWidth("borderBottomWidth") - getBorderWidth("borderTopWidth") - parseFloat(style.paddingBottom) - parseFloat(style.paddingTop);
            break;
        case "padding-box":
            box.width = box.width - getBorderWidth("borderLeftWidth") - getBorderWidth("borderRightWidth");
            box.height = box.height - getBorderWidth("borderBottomWidth") - getBorderWidth("borderTopWidth");
            break;
        case "border-box":
            break;
        case "margin-box":
            box.width = box.width + parseFloat(style.marginLeft) + parseFloat(style.marginRight);
            box.height = box.height + parseFloat(style.marginBottom) + parseFloat(style.marginTop);
            break;
        default:
            break;
    }

    return box;
};

Element.prototype.css = (function () {

    var thisObject = null;

    function getCssValue(cssRules) {
        cssRules = wkh.tools.preFixCss(cssRules) || cssRules;//如果无法得到浏览器相关cssRules，使用原值

        var computedStyle = thisObject.getComputedStyle();
        var computedValue = computedStyle[cssRules];//先得到计算值,兼容不同浏览器

        if (cssRules === "width") {//兼容在某些浏览器设置boxSizing返回值
            return thisObject.getBoxSize(computedStyle.boxSizing).width + "px";
        }

        if (cssRules === "height") {
            return thisObject.getBoxSize(computedStyle.boxSizing).height + "px";
        }

        if (computedValue === "auto") {//若为auto，则还要具体计算
            switch (cssRules) {
                case "left":
                    if (computedStyle.position === "relative") {
                        return computedStyle.right === "auto" ? "0px" : -parseFloat(computedStyle.right) + "px";
                    } else {
                        return thisObject.offsetLeft + "px";
                    }
                    break;
                case "top":
                    if (computedStyle.position === "relative") {
                        return computedStyle.bottom === "auto" ? "0px" : -parseFloat(computedStyle.bottom) + "px";
                    } else {
                        return thisObject.offsetTop + "px";
                    }
                    break;
                case "right":
                    if (computedStyle.position === "relative") {
                        return computedStyle.left === "auto" ? "0px" : -parseFloat(computedStyle.left) + "px";
                    } else {
                        return "0px";
                    }
                    break;
                case "bottom":
                    if (computedStyle.position === "relative") {
                        return computedStyle.top === "auto" ? "0px" : -parseFloat(computedStyle.top) + "px";
                    } else {
                        return "0px";//这两个还未完善
                    }
                    break;
            }
        }

        return computedValue;
    }

    function mergeCss(arr) {//合并css值，如padding:10px 10px 10px 10px合成10px，参数为数组
        if (arr[3] === arr[1]) arr.pop();
        else return arr;

        if (arr[2] === arr[0]) arr.pop();
        else return arr;

        if (arr[1] === arr[0]) arr.pop();
        else return arr;

        return arr;
    }

    function setCss(cssRule, value) {
        cssRule = wkh.tools.preFixCss(cssRule) || cssRule;
        try{
            thisObject.style[cssRule] = value;
        }catch(e){
        }
    }

    return function (cssRules, value) {//返回的函数
        if (!cssRules) {
            return;
        }
        thisObject = this;

        if (Object.classof(cssRules) === "Object") {
            for (var n in cssRules) {
                setCss(n, cssRules[n]);
            }
        } else if (value != null) {//赋值
            setCss(cssRules, value);
        } else {//返回值
            switch (cssRules) {
                case "font":
                    return [getCssValue("fontStyle"), getCssValue("fontVariant"), getCssValue("fontWeight"), getCssValue("fontSize"), getCssValue("fontFamily")].join(" ");
                case "padding": case "margin":
                    return mergeCss([getCssValue(cssRules + "Top"), getCssValue(cssRules + "Right"), getCssValue(cssRules + "Bottom"), getCssValue(cssRules + "Left")]).join(" ");
                case "outline": case "borderTop": case "borderRight": case "borderBottom": case "borderLeft":
                    return [getCssValue(cssRules + "Color"), getCssValue(cssRules + "Width"), getCssValue(cssRules + "Style")].join(" ");
                case "borderWidth": case "borderColor": case "borderStyle":
                    return mergeCss([getCssValue("borderTop" + cssRules.slice(6)), getCssValue("borderRight" + cssRules.slice(6)), getCssValue("borderBottom" + cssRules.slice(6)), getCssValue("borderLeft" + cssRules.slice(6))]).join(" ");
                case "border":
                    return this.css("borderTop");
                case "borderRadius":
                    return mergeCss([getCssValue("borderTopLeftRadius"), getCssValue("borderTopRightRadius"), getCssValue("borderBottomRightRadius"), getCssValue("borderBottomLeftRadius")]).join(" ");
                case "background":
                    return [getCssValue("backgroundColor"), getCssValue("backgroundImage"), getCssValue("backgroundRepeat"), getCssValue("backgroundAttachment"), getCssValue("backgroundPosition")].join(" ");
                default: return getCssValue(cssRules);
            }
        }
    };
}());

HTMLInputElement.prototype.selection = function (start, end) {
    if (start !== undefined) {
        if (!end) {
            end = this.value.length;
        }
        wkh.tools.setInputSelection(this, start, end);
    } else {
        wkh.tools.getInputSelection(this);
    }
};

if (!(document.head || document.getElementsByTagName("head")[0]).contains) {//兼容火狐浏览器的contains方法
    HTMLElement.prototype.contains = function (B) {
        return this.compareDocumentPosition(B) - 19 > 0;
    };
}

if (!(document.head || document.getElementsByTagName("head")[0]).removeNode) {//兼容removeNode方法
    HTMLElement.prototype.removeNode = function (deep) {
        if (deep === true) {
            return this.parentNode.removeChild(this);
        } else {
            var frame = document.createDocumentFragment();
            var children = this.childNodes;

            while (children.length) {
                frame.appendChild(children[0]);
            }

            this.parentNode.insertBefore(frame, this);
            return this.parentNode.removeChild(this);
        }
    };
}

Element.prototype.getContainsPath = function (other) {//返回由other到this元素的路径,路径数组为this到other元素
    var path = [other];

    if (this === other) {//如果other与this相等，直接返回path
    } else {
        try {
            while (other.parentNode !== this) {
                other = other.parentNode;
                path.unshift(other);
            }
        }
        catch (e) {
            return null;
        }
        path.unshift(this);
    }
    return path;
};

Element.prototype.one = function (type, handler, isCapture) {
    this.addEventListener(type, function tempHandler(e) {
        this.removeEventListener(type, tempHandler, isCapture);

        return handler(e);
    }, isCapture);
};

Element.prototype.trigger = function (type) {
    if ((type === "mouseenter" || type === "mouseleave") && this.onmouseenter === undefined) {
        if (type === "mouseenter") {
            type = "mouseover";
        } else {
            type = "mouseleave";
        }
    }
    if ((type === "onload" && this.onload === undefined) || (type === "onerror" && this.onerror === undefined)) {
        type = readystatechange;
    }
    if (document.createEvent) {
        var e = document.createEvent("HTMLEvents");
        e.initEvent(type, true, true);
        this.dispatchEvent(e);
    } else {
        this.fireEvent("on" + type);
    }
};

(function () {//拖动函数
    var dragElement = {};//the element need to be draged
    dragElement.proNum = 0;
    dragElement.length = 0;
    var nowDraged;

    var mousePageX, mousePageY;//the mouse point when element drag start
    var elementLeft, elementTop;

    var offsetX, offsetY;

    function mouseDownHandler(e) {
        e = e || window.event;

        if (e.type === "mousedown") {
            var target = e.target;
            var etargetDragNum;
            for (var i=0;i<dragElement.length;++i ) {
                if (dragElement[i].contains(target)) {
                    etargetDragNum = dragElement[i].__wkh_private_dragNum;
                    break;
                }
            }
            if ((etargetDragNum === undefined) || !(nowDraged = dragElement[etargetDragNum])) return;
            document.addEventListener("mouseup", mouseUpHandler, true);

            nowDraged.__wkh_private_cssText = nowDraged.style.cssText;
            nowDraged.css("userSelect", "none");
            nowDraged.addEventListener("selectstart", selectstart);//禁止选择
        } else {
            document.removeEventListener("mousemove", mouseDownHandler, true);
        }
        document.addEventListener("mousemove", moveHandler, true);

        var cssRules = nowDraged.getComputedStyle();

        var scroll = window.getScrollOffset();

        if (cssRules.position === "static") throw "元素定位为static，无法实现移动";
        else if (cssRules.position === "fixed") {
            mousePageX = e.clientX;
            mousePageY = e.clientY;
        } else {
            mousePageX = e.pageX || e.clientX + scroll.x;
            mousePageY = e.pageY || e.clientY + scroll.y;
        }
        var viewPortSize = window.getViewportSize();
        var box = nowDraged.getBoundingClientRect();

        elementLeft = parseFloat(nowDraged.css("left"));
        elementTop = parseFloat(nowDraged.css("top"));

        offsetX = elementLeft - mousePageX;
        offsetY = elementTop - mousePageY;
    }

    function moveHandler(e) {
        e = e || window.event;

        var scroll = window.getScrollOffset();
        if (nowDraged.getComputedStyle().position === "fixed") {
            mousePageX = e.clientX;
            mousePageY = e.clientY;
        } else {
            mousePageX = e.pageX || e.clientX + scroll.x;
            mousePageY = e.pageY || e.clientY + scroll.y;
        }

        nowDraged.__wkh_private_movetypeFn(mousePageX, offsetX, mousePageY, offsetY);
    }

    function moveX(x1, x2, y1, y2) {
        this.style.left = x1 + x2 + "px";
    }
    function moveY(x1,x2, y1, y2) {
        this.style.top = y1 + y2 + "px";
    }
    function moveAll(x1, x2, y1, y2) {
        this.style.left = x1 + x2 + "px";
        this.style.top = y1 + y2 + "px";
    }

    function mouseUpHandler(e) {
        document.removeEventListener("mousemove", moveHandler, true);
        document.removeEventListener("mouseup", mouseUpHandler, true);

        var left = nowDraged.css("left");
        var top = nowDraged.css("top");
        nowDraged.style.cssText = nowDraged.__wkh_private_cssText;
        nowDraged.css({
            left: left,
            top: top
        });
        nowDraged.removeEventListener("selectstart", selectstart);
    }

    function selectstart() {
        return false;
    }

    Element.prototype.startMove = function (moveType) {
        //if (this.getComputedStyle().position === "static") throw "元素定位为static，无法实现移动";
        if (!moveType) {
            moveType = "all";
        } else {
            moveType = moveType.toLowerCase();
        }
        switch (moveType) {
            case "all":
                this.__wkh_private_movetypeFn = moveAll;
                break;
            case "x":
                this.__wkh_private_movetypeFn = moveX;
                break;
            case "y":
                this.__wkh_private_movetypeFn = moveY;
                break;
            default:
                this.__wkh_private_movetypeFn = moveAll;
                break;
        }

        nowDraged = this;

        this.__wkh_private_cssText = nowDraged.style.cssText;
        this.css("userSelect", "none");
        this.addEventListener("selectstart", selectstart);//禁止选择

        document.addEventListener("mousemove", mouseDownHandler, true);

        var thisObj = this;
        document.addEventListener("click", function temp(e) {
            thisObj.stopMove();
            document.removeEventListener("click", temp, true);
        },true);
    };

    Element.prototype.stopMove = function () {
        nowDraged = null;

        var left = this.css("left");
        var top = this.css("top");
        this.style.cssText = this.__wkh_private_cssText;
        this.css({
            left: left,
            top: top
        });
        this.removeEventListener("selectstart", selectstart);

        document.removeEventListener("mousemove", moveHandler, true);
    };

    Element.prototype.drag = function (moveType) {
        if (!moveType) {
            moveType = "all";
        } else {
            moveType = moveType.toLowerCase();
        }
        switch (moveType) {
            case "all":
                this.__wkh_private_movetypeFn = moveAll;
                break;
            case "x":
                this.__wkh_private_movetypeFn = moveX;
                break;
            case "y":
                this.__wkh_private_movetypeFn = moveY;
                break;
            default:
                this.__wkh_private_movetypeFn = moveAll;
                break;
        }

        if (this.__wkh_private_dragNum === undefined) {
            this.__wkh_private_dragNum = dragElement.proNum++;
            dragElement.length++;
        }

        dragElement[this.__wkh_private_dragNum] = this;

        document.addEventListener("mousedown", mouseDownHandler, true);
    };

    Element.prototype.unDrag = function () {
        var etargetDraNum = this.__wkh_private_dragNum;
        if (etargetDraNum !== undefined) {
            delete dragElement[etargetDraNum];
            dragElement.length--;
        }

        if (nowDraged === this) {
            nowDraged = null;
        }

        if (dragElement.length === 0) {
            document.removeEventListener("mousedown", mouseDownHandler, true);
        }
    };
}());//元素拖动api


//js内置类型功能扩充
wkh.tools.setInnerKey(Object,"classof",function (o) {//获得对象的类属性
    if (o === null) return "Null";
    if (o === undefined) return "Undefined";
    return Object.prototype.toString.call(o).slice(8, -1);
});

wkh.tools.setInnerKey(Object.prototype, "classof", function () {
    return Object.classof(this);
});

wkh.tools.setInnerKey(Math, "factorial",
    wkh.tools.memorize(function (n) {//阶乘
        return (n <= 1) ? 1 : n * Math.factorial(n - 1);
    })
);

wkh.tools.setInnerKey(Math,"permutation",function (n, m) {//排列
    if (this.isInteger(n) && this.isInteger(m)) {
        if (n >= m && m >= 0 && n > 0) {
            return this.factorial(n) / this.factorial(n - m);
        } else {
            throw new RangeError("参数取值范围不正确");
        }
    }
});

wkh.tools.setInnerKey(Math,"combination",function (n, m) {//组合
    if (this.isInteger(n) && this.isInteger(m)) {
        if (n >= m && m >= 0 && n > 0) {
            return this.factorial(n) / this.factorial(n - m) / this.factorial(m);
        } else {
            throw new RangeError("参数取值范围不正确");
        }
    }
});

wkh.tools.setInnerKey(Math,"isInteger",function (num) {//判断是否为整数

    return (/^-?\d+$/).test(num.toString().trim());
});

wkh.tools.setInnerKey(Math,"isFloat",function (num) {//判断是否为实数

    return (/^(-?\d+)(\.\d+)?$/).test(num.toString().trim());
});

wkh.tools.setInnerKey(Math,"getBeziercurve",function (pointArr) {//取得贝塞尔曲线函数
    if (Object.classof(pointArr) !== "Array") {
        throw new TypeError("参数类型不正确");
    }

    var num = pointArr.length / 2;

    if (!this.isInteger(num) || num < 2) {
        throw new RangeError("数组元素个数为偶数个且至少四个（两个点）");
    }

    return function (t) {

        if (t < 0 || t > 1) {
            throw new RangeError("参数t值在0到1内");
        }
        var x = 0,
            y = 0;//返回坐标点

        var pow = num - 1;//次方

        for (var i = 0; i < num; ++i) {
            x += pointArr[i * 2] * Math.combination(pow, i) * Math.pow(t, i) * Math.pow(1 - t, pow - i);
            y += pointArr[i * 2 + 1] * Math.combination(pow, i) * Math.pow(t, i) * Math.pow(1 - t, pow-i);
        }

        return [x, y];
    };
});

wkh.tools.setInnerKey(String.prototype, "getByteLength", function () {//取得字符串字节长度
    return this.replace(/[^\x00-\xff]/g, 'xx').length;
});

(function () {
    wkh.tools.setInnerKey(Array.prototype, "contains", function(member) {//是否含有相应的元素
        var length = this.length;
        for (var i = 0; i < length; ++i) {
            if (this[i] === member) {
                return i;
            }
        }

        return -1;
    });
}());

(function () {
    wkh.tools.setInnerKey(Array.prototype, "remove", function(index) {//移除数组元素，返回被移除元素
        if (Object.classof(index) !== "Number") {
            return Array.prototype.remove.call(this, Array.prototype.contains.call(this, index));
        }
        var length = this.length;
        if (index < 0 || index > length) {
            return;
        }

        var returnVal = this[index];

        for (var i = index; i < length - 1; ++i) {
            this[i] = this[i + 1];
        }
        Array.prototype.pop.call(this);

        return returnVal;
    });
}());

(function () {
    wkh.tools.setInnerKey(Array.prototype, "clone", function () {//复制数组
        return Array.prototype.slice.call(this, 0);
    });
}());

(function () {
    breakError = new Error("StopIteration");
    wkh.tools.setInnerKey(Array, "stop", function () {
        throw breakError;
    });
    wkh.tools.setInnerKey(Array.prototype,"forEachB", function(fn, thisObj) {//可中途断出的forEach方法
        try {
            if (!this.forEach) {//如果没有forEach函数的类数组对象
                Array.prototype.forEach.call(this, fn, thisObj);
            } else {
                this.forEach(fn, thisObj);
            }
        } catch (e) {
            if (e === breakError) {
                return;
            } else {
                throw e;
            }
        }
    });
}());